﻿using Quartz;
using Quartz.Impl;
using SUNRPA.CONTROLS.IMPL.domain;
using SUNRPA.CONTROLS.IMPL.domain.quartz;
using System.Runtime.InteropServices;
using System.Windows;

namespace SUNRPA.CONTROLS.IMPL.util {
    /// <summary>
    ///IScheduler ：和调度器交互的主要接口
    ///JobBuilder：定义 JobDetail 实例
    ///IJobDetail：定义 Job 实例及其它相关的静态信息
    ///IJob：自定义的作业模块所要继承的接口，调度器会调用这个实现
    ///TriggerBuilder：定义 Trigger 实例
    ///ITrigger：定义 Job 被触发的时间
    ///
    ///1、创建任务调度器。调度器通常在应用程序启动时创建，一个应用程序实例通常只需要一个调度器即可。
    ///2、创建Job和JobDetail。Job是作业的类型，描述了作业是如何执行的，这个类是由我们定义的；JobDetail是Quartz对作业的封装，它包含Job类型，以及Job在执行时用到的数据，还包括是否要持久化、是否覆盖已存在的作业等选项。
    ///3、创建触发器。触发器描述了在何时执行作业。
    ///4、添加调度。当完成以上三步以后，就可以对作业进行调度了。
    /// </summary>
    public class ScheduleUtil {
        private CancellationTokenSource ct = new CancellationTokenSource();
        private static IScheduler scheduler;
        private static ScheduleUtil scheduleUtil;

        public IScheduler Scheduler {get => scheduler; set => scheduler = value; }

        public CancellationTokenSource Ct { get => ct; set => ct = value;}
        private ScheduleUtil() {
            Task<IScheduler> sk = InitScheduler();
            scheduler = sk.Result;
            scheduler.Start();
        }

        public static ScheduleUtil getInstance() {
            lock(typeof(ScheduleUtil)){
                if(scheduleUtil == null) { 
                    scheduleUtil = new ScheduleUtil();
                } 
                return scheduleUtil;
            }
        }

        private async Task<IScheduler> InitScheduler() { 
            StdSchedulerFactory factory = new StdSchedulerFactory();
            IScheduler scheduler = await factory.GetScheduler(ct.Token);
            return scheduler;
        }
        
        public void SetJob(IJobDetail job, ITrigger trigger) { 
            scheduler.ScheduleJob(job, trigger);
        }

        public static void UpdateTrigger(Trigger<TimeTriggerEntry> trigger, Window hiddenWindow, [Optional]string originalName, [Optional]string originalProcessName) {
            if(string.IsNullOrEmpty(originalName) || string.IsNullOrEmpty(originalProcessName)) { 
                scheduler.DeleteJob(new JobKey(trigger.processInfo.processName + "-" + trigger.triggerName));
            } else { 
                scheduler.DeleteJob(new JobKey(originalProcessName + "-" + originalName));    
            }
            ScheduleUtil.InsertTrigger(trigger, hiddenWindow);
        }

        public static void InsertTrigger(Trigger<TimeTriggerEntry> trigger, Window hiddenWindow) { 
            if(trigger.enable ?? false) { 
                TimeTriggerEntry entry = trigger.triggerEntry;
                if(entry.isTimeEnded ?? false && entry.endDateTime <= DateTime.Now) { 
                    return;
                }
                IJobDetail job = JobBuilder.Create<FlowExeJob>().WithIdentity(trigger.processInfo.processName + "-" + trigger.triggerName)
                    .SetJobData(new JobDataMap { 
                        new KeyValuePair<string, object>("processInfo", trigger.processInfo),
                        new KeyValuePair<string, object>("mainWindow", hiddenWindow),
                    }).Build();
                TriggerBuilder tbuilder = TriggerBuilder.Create()
                    .WithIdentity(trigger.triggerName)
                    .WithSchedule(CronScheduleBuilder.CronSchedule(entry.GenerateExpression()).WithMisfireHandlingInstructionDoNothing())
                    .StartNow();
                if(entry.isTimeEnded ?? false) { 
                    tbuilder.EndAt(entry.endDateTime);
                }
                scheduleUtil.SetJob(job, tbuilder.Build());
            }
        }

        public static void DeleteTrigger(Trigger<TimeTriggerEntry> trigger) {
            if(trigger.processInfo != null) { 
                scheduler.DeleteJob(new JobKey(trigger.processInfo.processName + "-" + trigger.triggerName));
            }
        }
    }
}
